﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace DogDemo
{
    public abstract class Sprite
    {
        #region CONSTS
        private const float CONST_DEFAULT_FRAME_TIME = 0.1f;
        private const bool CONST_DEFAULT_EXPIRED = false;
        private const bool CONST_DEFAULT_ANIMATED = true;
        private const bool CONST_DEFAULT_ANIMATE_WHEN_STOPPED = true;
        private const bool CONST_DEFAULT_COLLIDABLE = true;
        #endregion

        #region Declarations
        private Texture2D _texture;
        public Texture2D Texture
        {
            get { return _texture; }
            set { _texture = value; }
        }

        private Vector2 _worldLocation;

        private Vector2 _velocity;
        public Vector2 Velocity
        {
            get { return _velocity; }
            set { _velocity = value; }
        }

        private List<Rectangle> _frames = new List<Rectangle>();
        public List<Rectangle> Frames
        {
            get { return _frames; }
            set { _frames = value; }
        }

        private int _currentFrame;
        public int CurrentFrame
        {
            get { return _currentFrame; }
            set { _currentFrame = value; }
        }

        private float _frameTime;
        public float FrameTime
        {
            get { return _frameTime; }
            set { _frameTime = value; }
        }

        private float _timeForCurrentFrame;
        public float TimeForCurrentFrame
        {
            get { return _timeForCurrentFrame; }
            set { _timeForCurrentFrame = value; }
        }

        private Color _tintColor;
        public Color TintColor
        {
            get { return _tintColor; }
            set { _tintColor = value; }
        }

        private float rotation;
        public float Rotation
        {
            get { return rotation; }
            set { rotation = value; }
        }

        private bool _expired;
        public bool Expired
        {
            get { return _expired; }
            set { _expired = value; }
        }

        private bool _animated;
        public bool Animated
        {
            get { return _animated; }
            set { _animated = value; }
        }

        private bool _animateWhenStopped;
        public bool AnimateWhenStopped
        {
            get { return _animateWhenStopped; }
            set { _animateWhenStopped = value; }
        }

        private bool _collidable;
        public bool Collidable
        {
            get { return _collidable; }
            set { _collidable = value; }
        }

        private int _collisionRadius;
        public int CollisionRadius
        {
            get { return _collisionRadius; }
            set { _collisionRadius = value; }
        }

        private int _boundingXPadding;
        public int BoundingXPadding
        {
            get { return _boundingXPadding; }
            set { _boundingXPadding = value; }
        }
        private int _boundingYPadding;
        public int BoundingYPadding
        {
            get { return _boundingYPadding; }
            set { _boundingYPadding = value; }
        }
        #endregion

        #region Drawing an Animation Properties
        public int FrameWidth
        {
            get { return _frames[0].Width; }
        }

        public int FrameHeight
        {
            get { return _frames[0].Height; }
        }

        public int Frame
        {
            get { return _currentFrame; }
            set
            {
                _currentFrame = (int)MathHelper.Clamp(value, 0, _frames.Count - 1);
            }
        }

        public Rectangle Source
        {
            get { return _frames[_currentFrame]; }
        }
        #endregion

        #region Positional Properties
        public Vector2 WorldLocation
        {
            get { return _worldLocation; }
            set { _worldLocation = value; }
        }

        public Vector2 ScreenLocation
        {
            get
            {
                return Camera.Transform(_worldLocation);
            }
        }

        public Rectangle WorldRectangle
        {
            get
            {
                return new Rectangle((int)_worldLocation.X, (int)_worldLocation.Y, FrameWidth, FrameHeight);
            }
        }

        public Rectangle ScreenRectangle
        {
            get
            {
                return Camera.Transform(WorldRectangle);
            }
        }

        public Vector2 RelativeCenter
        {
            get { return new Vector2(FrameWidth / 2, FrameHeight / 2); }
        }

        public Vector2 WorldCenter
        {
            get { return _worldLocation + RelativeCenter; }
        }

        public Vector2 ScreenCenter
        {
            get
            {
                return Camera.Transform(_worldLocation + RelativeCenter);
            }
        }
        #endregion

        #region Collision Related Properties
        public Rectangle BoundingBoxRect
        {
            get
            {
                return new Rectangle((int)_worldLocation.X, (int)_worldLocation.Y, FrameWidth, FrameHeight);
            }
        }

        #endregion

        #region Constructors
        public Sprite(Vector2 worldLocation, Texture2D texture, Rectangle initialFrame, Vector2 velocity)
        {
            this._worldLocation = worldLocation;
            Texture = texture;
            this._velocity = velocity;
            this._frames = new List<Rectangle>();
            this._frames.Add(initialFrame);
            this._frameTime = CONST_DEFAULT_FRAME_TIME;
            this._tintColor = Color.White;
            this._expired = CONST_DEFAULT_EXPIRED;
            this._animated = CONST_DEFAULT_ANIMATED;
            this._animateWhenStopped = CONST_DEFAULT_ANIMATE_WHEN_STOPPED;
            this._collidable = CONST_DEFAULT_COLLIDABLE;
        }
        #endregion

        #region Collision methods
        public bool IsBoxColliding(Rectangle OtherBox)
        {
            if ((Collidable) && (!Expired))
            {
                return BoundingBoxRect.Intersects(OtherBox);
            }
            else
            {
                return false;
            }
        }

        public bool IsCircleColliding(Vector2 otherCenter, float otherRadius)
        {
            if ((Collidable) && (!Expired))
            {
                if (Vector2.Distance(WorldCenter, otherCenter) < (CollisionRadius + otherRadius))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        #endregion

        #region Animation-related Methods
        public void AddFrame(Rectangle frameRectangle)
        {
            _frames.Add(frameRectangle);
        }

        public void RotateTo(Vector2 direction)
        {
            Rotation = (float)Math.Atan2(direction.Y, direction.X);
        }
        #endregion

        #region Update and Draw Methods
        public virtual void Update(GameTime gameTime)
        {
            if (!Expired)
            {
                float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
                _timeForCurrentFrame += elapsed;
                if (Animated)
                {
                    if (_timeForCurrentFrame >= _frameTime)
                    {
                        if ((AnimateWhenStopped) || (_velocity != Vector2.Zero))
                        {
                            _currentFrame = (_currentFrame + 1) % (_frames.Count);
                            _timeForCurrentFrame = 0.0f;
                        }
                    }
                }
                _worldLocation += (_velocity * elapsed);
            }
        }

        public virtual void DoDraw(SpriteBatch spriteBatch)
        {
            if (!Expired)
            {
                if (Camera.ObjectIsVisible(WorldRectangle))
                {
                    spriteBatch.Draw(Texture, ScreenCenter, Source, _tintColor, rotation, RelativeCenter, 1.0f, SpriteEffects.None, 0.0f);
                }
            }
        }
        #endregion
    }

}
